import asyncio

import requests

"""
Python 中使用协程最常用的库莫过于 asyncio，所以本节会以 asyncio 为基础来介绍协程的用法。

首先，我们需要了解下面几个概念：

event_loop：事件循环，相当于一个无限循环，我们可以把一些函数注册到这个事件循环上，当满足条件发生的时候，就会调用对应的处理方法。
coroutine：中文翻译叫协程，在 Python 中常指代协程对象类型，我们可以将协程对象注册到时间循环中，它会被事件循环调用。我们可以使用 async 关键字来定义一个方法，这个方法在调用时不会立即被执行，而是返回一个协程对象。
task：任务，它是对协程对象的进一步封装，包含了任务的各个状态。
future：代表将来执行或没有执行的任务的结果，实际上和 task 没有本质区别。
另外，我们还需要了解 async/await 关键字，它是从 Python 3.5 才出现的，专门用于定义协程。其中，async 定义一个协程，await 用来挂起阻塞方法的执行。

"""


"""
 示例1 定义协程1
 async 定义的方法就会变成一个无法直接执行的 coroutine 对象，必须将其注册到事件循环中才可以执行。
"""

# async def execute(x):
#     print('Number:', x)
#
# # 调用了这个方法，然而这个方法并没有执行，而是返回了一个 coroutine 协程对象
# coroutine = execute(1)
# print('Coroutine:', coroutine)
# print('After calling execute')
#
# # 创建了一个事件循环 loop
# loop = asyncio.get_event_loop()
#
# # 将协程注册到事件循环 loop 中，然后启动
# loop.run_until_complete(coroutine)
# print('After calling loop')


"""
 示例2 定义协程2
 create_task 方法将 coroutine 对象转化为 task 对象   它是对 coroutine 对象的进一步封装，比 coroutine 对象多了运行状态，比如 running、finished 等
"""

# async def execute(x):
#     print('Number:', x)
#
# coroutine = execute(1)
# print('Coroutine:', coroutine)
# print('After calling execute')
# loop = asyncio.get_event_loop()
# task = loop.create_task(coroutine) # create_task 方法将 coroutine 对象转化为 task 对象
# print('Task:', task)# 此时任务是pending 状态
# loop.run_until_complete(task)
# print('Task:', task) # 执行完毕后，任务是finished
# print('After calling loop')


"""
 示例3 定义协程 -推荐
 task 可以不通过loop对象创建， 通过asyncio.ensure_future 将 coroutine 对象转化为 task 对象   
"""

# async def execute(x):
#     print('Number:', x)
#
# coroutine = execute(1)
# print('Coroutine:', coroutine)
# print('After calling execute')
# task = asyncio.ensure_future(coroutine) # ensure_future 方法将 coroutine 对象转化为 task 对象
# loop = asyncio.get_event_loop()
# print('Task:', task)# 此时任务是pending 状态
# loop.run_until_complete(task)
# print('Task:', task) # 执行完毕后，任务是finished
# print('After calling loop')


"""
  示例4 定义回调-获取任务执行结果  给任务执行结束后，执行回调函数
  task.add_done_callback 
"""

# async def request():
#     url = 'https://www.baidu.com'
#     res = requests.get(url)
#     return res
#
# def callback(task):
#     # 就是上面request方法 执行后的返回结果
#     print('response',task.result())
#
#
# coroutine = request()
# task = asyncio.ensure_future(coroutine) # ensure_future 方法将 coroutine 对象转化为 task 对象
#
# # 定义回调函数，这样当 task 执行完毕之后，就可以调用 callback 方法了。同时 task 对象还会作为参数传递给 callback 方法，调用 task 对象的 result 方法就可以获取返回结果了
# task.add_done_callback(callback)
# loop = asyncio.get_event_loop()
# print('Task:', task)# 此时任务是pending 状态
# loop.run_until_complete(task)
# print('Task:', task) # 执行完毕后，任务是finished



"""
  示例5 不定义回调函数-获取任务执行结果
  实际上不用回调方法，直接在 task 运行完毕之后，也可以直接调用 result 方法获取结果，如下所示：
"""
# async def request():
#     url = 'https://www.baidu.com'
#     res = requests.get(url)
#     return res
#
#
# coroutine = request()
# task = asyncio.ensure_future(coroutine) # ensure_future 方法将 coroutine 对象转化为 task 对象
# loop = asyncio.get_event_loop()
# print('Task:', task)# 此时任务是pending 状态
# loop.run_until_complete(task)
# print('Task:', task) # 执行完毕后，任务是finished
# print('result',task.result())


"""
  示例6 多任务协程
  如果想执行多次请求，应该怎么办呢？我们可以定义一个 task 列表，然后使用 asyncio 的 wait 方法即可执行
  
  asyncio.wait(tasks)
  是 asyncio 框架中用于“等待一组协程（或 Future/Task）对象全部完成”的工具。
  它会"并发地"运行这些任务，直到所有任务都结束（默认模式下）。
"""
async def request():
    url = 'https://www.baidu.com'
    status = requests.get(url)
    return status

# 可以定义一个 task 列表
# 这边用了列表推导式，循环每执行1次，都会创建一个task
tasks = [asyncio.ensure_future(request()) for _ in range(5)]
print('Tasks:', tasks)

loop = asyncio.get_event_loop()
# 用 asyncio 的 wait 方法即可执行
loop.run_until_complete(asyncio.wait(tasks))

for task in tasks:
    print('Task Result:', task.result())



